home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-21  |  61.7 KB  |  1,973 lines

  1. /* 
  2.  * tkButton.c --
  3.  *
  4.  *    This module implements a collection of button-like
  5.  *    widgets for the Tk toolkit.  The widgets implemented
  6.  *    include labels, buttons, check buttons, and radio
  7.  *    buttons.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tkButton.c 1.128 96/03/01 17:34:49
  16.  */
  17.  
  18. #include "default.h"
  19. #include "tkPort.h"
  20. #include "tkInt.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the button.  NULL
  29.                  * means that the window has been destroyed. */
  30.     Display *display;        /* Display containing widget.  Needed to
  31.                  * free up resources after tkwin is gone. */
  32.     Tcl_Interp *interp;        /* Interpreter associated with button. */
  33.     Tcl_Command widgetCmd;    /* Token for button's widget command. */
  34.     int type;            /* Type of widget:  restricts operations
  35.                  * that may be performed on widget.  See
  36.                  * below for possible values. */
  37.  
  38.     /*
  39.      * Information about what's in the button.
  40.      */
  41.  
  42.     char *text;            /* Text to display in button (malloc'ed)
  43.                  * or NULL. */
  44.     int numChars;        /* # of characters in text. */
  45.     int underline;        /* Index of character to underline.  < 0 means
  46.                  * don't underline anything. */
  47.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  48.                  * If non-NULL, button displays the contents
  49.                  * of this variable. */
  50.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  51.                  * then text and textVar are ignored. */
  52.     char *imageString;        /* Name of image to display (malloc'ed), or
  53.                  * NULL.  If non-NULL, bitmap, text, and
  54.                  * textVarName are ignored. */
  55.     Tk_Image image;        /* Image to display in window, or NULL if
  56.                  * none. */
  57.     char *selectImageString;    /* Name of image to display when selected
  58.                  * (malloc'ed), or NULL. */
  59.     Tk_Image selectImage;    /* Image to display in window when selected,
  60.                  * or NULL if none.  Ignored if image is
  61.                  * NULL. */
  62.  
  63.     /*
  64.      * Information used when displaying widget:
  65.      */
  66.  
  67.     Tk_Uid state;        /* State of button for display purposes:
  68.                  * normal, active, or disabled. */
  69.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  70.                  * border and background when window
  71.                  * isn't active.  NULL means no such
  72.                  * border exists. */
  73.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  74.                  * border and background when window
  75.                  * is active.  NULL means no such
  76.                  * border exists. */
  77.     int borderWidth;        /* Width of border. */
  78.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  79.     int highlightWidth;        /* Width in pixels of highlight to draw
  80.                  * around widget when it has the focus.
  81.                  * <= 0 means don't draw a highlight. */
  82.     XColor *highlightBgColorPtr;
  83.                 /* Color for drawing traversal highlight
  84.                  * area when highlight is off. */
  85.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  86.     int inset;            /* Total width of all borders, including
  87.                  * traversal highlight and 3-D border.
  88.                  * Indicates how much interior stuff must
  89.                  * be offset from outside edges to leave
  90.                  * room for borders. */
  91.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  92.     XColor *normalFg;        /* Foreground color in normal mode. */
  93.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  94.                  * means use normalFg instead. */
  95.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  96.                  * means use normalFg with a 50% stipple
  97.                  * instead. */
  98.     GC normalTextGC;        /* GC for drawing text in normal mode.  Also
  99.                  * used to copy from off-screen pixmap onto
  100.                  * screen. */
  101.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  102.                  * means use normalTextGC). */
  103.     Pixmap gray;        /* Pixmap for displaying disabled text if
  104.                  * disabledFg is NULL. */
  105.     GC disabledGC;        /* Used to produce disabled effect.  If
  106.                  * disabledFg isn't NULL, this GC is used to
  107.                  * draw button text or icon.  Otherwise
  108.                  * text or icon is drawn with normalGC and
  109.                  * this GC is used to stipple background
  110.                  * across it.  For labels this is None. */
  111.     GC copyGC;            /* Used for copying information from an
  112.                  * off-screen pixmap to the screen. */
  113.     char *widthString;        /* Value of -width option.  Malloc'ed. */
  114.     char *heightString;        /* Value of -height option.  Malloc'ed. */
  115.     int width, height;        /* If > 0, these specify dimensions to request
  116.                  * for window, in characters for text and in
  117.                  * pixels for bitmaps.  In this case the actual
  118.                  * size of the text string or bitmap is
  119.                  * ignored in computing desired window size. */
  120.     int wrapLength;        /* Line length (in pixels) at which to wrap
  121.                  * onto next line.  <= 0 means don't wrap
  122.                  * except at newlines. */
  123.     int padX, padY;        /* Extra space around text (pixels to leave
  124.                  * on each side).  Ignored for bitmaps and
  125.                  * images. */
  126.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  127.                  * inside button region. */
  128.     Tk_Justify justify;        /* Justification to use for multi-line text. */
  129.     int indicatorOn;        /* True means draw indicator, false means
  130.                  * don't draw it. */
  131.     Tk_3DBorder selectBorder;    /* For drawing indicator background, or perhaps
  132.                  * widget background, when selected. */
  133.     int textWidth;        /* Width needed to display text as requested,
  134.                  * in pixels. */
  135.     int textHeight;        /* Height needed to display text as requested,
  136.                  * in pixels. */
  137.     int indicatorSpace;        /* Horizontal space (in pixels) allocated for
  138.                  * display of indicator. */
  139.     int indicatorDiameter;    /* Diameter of indicator, in pixels. */
  140.  
  141.     /*
  142.      * For check and radio buttons, the fields below are used
  143.      * to manage the variable indicating the button's state.
  144.      */
  145.  
  146.     char *selVarName;        /* Name of variable used to control selected
  147.                  * state of button.  Malloc'ed (if
  148.                  * not NULL). */
  149.     char *onValue;        /* Value to store in variable when
  150.                  * this button is selected.  Malloc'ed (if
  151.                  * not NULL). */
  152.     char *offValue;        /* Value to store in variable when this
  153.                  * button isn't selected.  Malloc'ed
  154.                  * (if not NULL).  Valid only for check
  155.                  * buttons. */
  156.  
  157.     /*
  158.      * Miscellaneous information:
  159.      */
  160.  
  161.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  162.     char *takeFocus;        /* Value of -takefocus option;  not used in
  163.                  * the C code, but used by keyboard traversal
  164.                  * scripts.  Malloc'ed, but may be NULL. */
  165.     char *command;        /* Command to execute when button is
  166.                  * invoked; valid for buttons only.
  167.                  * If not NULL, it's malloc-ed. */
  168.     int flags;            /* Various flags;  see below for
  169.                  * definitions. */
  170. #ifdef STk_CODE
  171.     int string;            /* if true the value of the variable associated
  172.                   * to the button must be "stringified"
  173.                  */
  174. #endif
  175. } Button;
  176.  
  177. /*
  178.  * Possible "type" values for buttons.  These are the kinds of
  179.  * widgets supported by this file.  The ordering of the type
  180.  * numbers is significant:  greater means more features and is
  181.  * used in the code.
  182.  */
  183.  
  184. #define TYPE_LABEL        0
  185. #define TYPE_BUTTON        1
  186. #define TYPE_CHECK_BUTTON    2
  187. #define TYPE_RADIO_BUTTON    3
  188.  
  189. /*
  190.  * Class names for buttons, indexed by one of the type values above.
  191.  */
  192.  
  193. static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
  194.  
  195. /*
  196.  * Flag bits for buttons:
  197.  *
  198.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  199.  *                has already been queued to redraw
  200.  *                this window.
  201.  * SELECTED:            Non-zero means this button is selected,
  202.  *                so special highlight should be drawn.
  203.  * GOT_FOCUS:            Non-zero means this button currently
  204.  *                has the input focus.
  205.  */
  206.  
  207. #define REDRAW_PENDING        1
  208. #define SELECTED        2
  209. #define GOT_FOCUS        4
  210.  
  211. /*
  212.  * Mask values used to selectively enable entries in the
  213.  * configuration specs:
  214.  */
  215.  
  216. #define LABEL_MASK        TK_CONFIG_USER_BIT
  217. #define BUTTON_MASK        TK_CONFIG_USER_BIT << 1
  218. #define CHECK_BUTTON_MASK    TK_CONFIG_USER_BIT << 2
  219. #define RADIO_BUTTON_MASK    TK_CONFIG_USER_BIT << 3
  220. #define ALL_MASK        (LABEL_MASK | BUTTON_MASK \
  221.     | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
  222.  
  223. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  224.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  225.  
  226. /*
  227.  * Information used for parsing configuration specs:
  228.  */
  229.  
  230. static Tk_ConfigSpec configSpecs[] = {
  231.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  232.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
  233.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  234.     |TK_CONFIG_COLOR_ONLY},
  235.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  236.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
  237.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  238.     |TK_CONFIG_MONO_ONLY},
  239.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  240.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), 
  241.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  242.     |TK_CONFIG_COLOR_ONLY},
  243.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  244.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), 
  245.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  246.     |TK_CONFIG_MONO_ONLY},
  247.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  248.     DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
  249.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  250.     DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
  251.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  252.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  253.     DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
  254.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  255.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  256.     (char *) NULL, 0, ALL_MASK},
  257.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  258.     (char *) NULL, 0, ALL_MASK},
  259.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  260.     DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
  261.     ALL_MASK|TK_CONFIG_NULL_OK},
  262.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  263.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
  264. #ifdef STk_CODE
  265.     {TK_CONFIG_CLOSURE, "-command", "command", "Command",
  266. #else
  267.     {TK_CONFIG_STRING, "-command", "command", "Command",
  268. #endif
  269.     DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
  270.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  271.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  272.     DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
  273.     ALL_MASK|TK_CONFIG_NULL_OK},
  274.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  275.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  276.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  277.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  278.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  279.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  280.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  281.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  282.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  283.     (char *) NULL, 0, ALL_MASK},
  284.     {TK_CONFIG_FONT, "-font", "font", "Font",
  285.     DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
  286.     ALL_MASK},
  287.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  288.     DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
  289. #ifdef STk_CODE
  290.     {TK_CONFIG_SINT, "-height", "height", "Height",
  291. #else
  292.     {TK_CONFIG_STRING, "-height", "height", "Height",
  293. #endif
  294.     DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
  295.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  296.     "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
  297.     Tk_Offset(Button, highlightBgColorPtr), ALL_MASK},
  298.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  299.     DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
  300.     ALL_MASK},
  301.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  302.     "HighlightThickness",
  303.     DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  304.     LABEL_MASK},
  305.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  306.     "HighlightThickness",
  307.     DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  308.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  309.     {TK_CONFIG_STRING, "-image", "image", "Image",
  310.     DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
  311.     ALL_MASK|TK_CONFIG_NULL_OK},
  312.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  313.     DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
  314.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  315.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  316.     DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), ALL_MASK},
  317. #ifdef STk_CODE
  318.     {TK_CONFIG_BSTRING, "-offvalue", "offValue", "Value",
  319. #else
  320.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  321. #endif
  322.     DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
  323.     CHECK_BUTTON_MASK},
  324. #ifdef STk_CODE
  325.     {TK_CONFIG_BSTRING, "-onvalue", "onValue", "Value",
  326. #else
  327.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  328. #endif
  329.     DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
  330.     CHECK_BUTTON_MASK},
  331.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  332.     DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK},
  333.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  334.     DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
  335.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  336.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  337.     DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK},
  338.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  339.     DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
  340.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  341.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  342.     DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
  343.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  344.     DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
  345.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  346.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  347.     DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
  348.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  349.     |TK_CONFIG_NULL_OK},
  350.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  351.     DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
  352.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  353.     |TK_CONFIG_NULL_OK},
  354.     {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
  355.     DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
  356.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  357.     {TK_CONFIG_UID, "-state", "state", "State",
  358.     DEF_BUTTON_STATE, Tk_Offset(Button, state),
  359.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  360. #ifdef STk_CODE
  361.     {TK_CONFIG_BOOLEAN, "-stringvalue", "stringvalue", "StringValue",
  362.     "#f", Tk_Offset(Button, string),
  363.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  364. #endif
  365. #ifdef STk_CODE
  366.     {TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
  367. #else
  368.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  369. #endif
  370.     DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  371.     LABEL_MASK|TK_CONFIG_NULL_OK},
  372. #ifdef STk_CODE
  373.     {TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
  374. #else
  375.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  376. #endif
  377.     DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  378.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  379.     {TK_CONFIG_STRING, "-text", "text", "Text",
  380.     DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
  381.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  382.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
  383.     ALL_MASK|TK_CONFIG_NULL_OK},
  384.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  385.     DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), ALL_MASK},
  386. #ifdef STk_CODE
  387.     {TK_CONFIG_BSTRING, "-value", "value", "Value",
  388. #else
  389.     {TK_CONFIG_STRING, "-value", "value", "Value",
  390. #endif
  391.     DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
  392.     RADIO_BUTTON_MASK},
  393.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  394.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  395.     RADIO_BUTTON_MASK},
  396.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  397.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  398.     CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
  399. #ifdef STk_CODE
  400.     {TK_CONFIG_SINT, "-width", "width", "Width",
  401. #else
  402.     {TK_CONFIG_STRING, "-width", "width", "Width",
  403. #endif
  404.     DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
  405.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  406.     DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), ALL_MASK},
  407.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  408.     (char *) NULL, 0, 0}
  409. };
  410.  
  411. /*
  412.  * String to print out in error messages, identifying options for
  413.  * widget commands for different types of labels or buttons:
  414.  */
  415.  
  416. static char *optionStrings[] = {
  417.     "cget or configure",
  418.     "cget, configure, flash, or invoke",
  419.     "cget, configure, deselect, flash, invoke, select, or toggle",
  420.     "cget, configure, deselect, flash, invoke, or select"
  421. };
  422.  
  423. /*
  424.  * Forward declarations for procedures defined later in this file:
  425.  */
  426.  
  427. static void        ButtonCmdDeletedProc _ANSI_ARGS_((
  428.                 ClientData clientData));
  429. static int        ButtonCreate _ANSI_ARGS_((ClientData clientData,
  430.                 Tcl_Interp *interp, int argc, char **argv,
  431.                 int type));
  432. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  433.                 XEvent *eventPtr));
  434. static void        ButtonImageProc _ANSI_ARGS_((ClientData clientData,
  435.                 int x, int y, int width, int height,
  436.                 int imgWidth, int imgHeight));
  437. static void        ButtonSelectImageProc _ANSI_ARGS_((
  438.                 ClientData clientData, int x, int y, int width,
  439.                 int height, int imgWidth, int imgHeight));
  440. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  441.                 Tcl_Interp *interp, char *name1, char *name2,
  442.                 int flags));
  443. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  444.                 Tcl_Interp *interp, char *name1, char *name2,
  445.                 int flags));
  446. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  447.                 Tcl_Interp *interp, int argc, char **argv));
  448. static void        ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
  449. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  450.                 Button *butPtr, int argc, char **argv,
  451.                 int flags));
  452. static void        DestroyButton _ANSI_ARGS_((Button *butPtr));
  453. static void        DisplayButton _ANSI_ARGS_((ClientData clientData));
  454. static int        InvokeButton  _ANSI_ARGS_((Button *butPtr));
  455.  
  456. /*
  457.  *--------------------------------------------------------------
  458.  *
  459.  * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
  460.  *
  461.  *    These procedures are invoked to process the "button", "label",
  462.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  463.  *    user documentation for details on what they do.
  464.  *
  465.  * Results:
  466.  *    A standard Tcl result.
  467.  *
  468.  * Side effects:
  469.  *    See the user documentation.  These procedures are just wrappers;
  470.  *    they call ButtonCreate to do all of the real work.
  471.  *
  472.  *--------------------------------------------------------------
  473.  */
  474.  
  475. int
  476. Tk_ButtonCmd(clientData, interp, argc, argv)
  477.     ClientData clientData;    /* Main window associated with
  478.                  * interpreter. */
  479.     Tcl_Interp *interp;        /* Current interpreter. */
  480.     int argc;            /* Number of arguments. */
  481.     char **argv;        /* Argument strings. */
  482. {
  483.     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
  484. }
  485.  
  486. int
  487. Tk_CheckbuttonCmd(clientData, interp, argc, argv)
  488.     ClientData clientData;    /* Main window associated with
  489.                  * interpreter. */
  490.     Tcl_Interp *interp;        /* Current interpreter. */
  491.     int argc;            /* Number of arguments. */
  492.     char **argv;        /* Argument strings. */
  493. {
  494.     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
  495. }
  496.  
  497. int
  498. Tk_LabelCmd(clientData, interp, argc, argv)
  499.     ClientData clientData;    /* Main window associated with
  500.                  * interpreter. */
  501.     Tcl_Interp *interp;        /* Current interpreter. */
  502.     int argc;            /* Number of arguments. */
  503.     char **argv;        /* Argument strings. */
  504. {
  505.     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
  506. }
  507.  
  508. int
  509. Tk_RadiobuttonCmd(clientData, interp, argc, argv)
  510.     ClientData clientData;    /* Main window associated with
  511.                  * interpreter. */
  512.     Tcl_Interp *interp;        /* Current interpreter. */
  513.     int argc;            /* Number of arguments. */
  514.     char **argv;        /* Argument strings. */
  515. {
  516.     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
  517. }
  518.  
  519. /*
  520.  *--------------------------------------------------------------
  521.  *
  522.  * ButtonCreate --
  523.  *
  524.  *    This procedure does all the real work of implementing the
  525.  *    "button", "label", "radiobutton", and "checkbutton" Tcl
  526.  *    commands.  See the user documentation for details on what it does.
  527.  *
  528.  * Results:
  529.  *    A standard Tcl result.
  530.  *
  531.  * Side effects:
  532.  *    See the user documentation.
  533.  *
  534.  *--------------------------------------------------------------
  535.  */
  536.  
  537. static int
  538. ButtonCreate(clientData, interp, argc, argv, type)
  539.     ClientData clientData;    /* Main window associated with
  540.                  * interpreter. */
  541.     Tcl_Interp *interp;        /* Current interpreter. */
  542.     int argc;            /* Number of arguments. */
  543.     char **argv;        /* Argument strings. */
  544.     int type;            /* Type of button to create: TYPE_LABEL,
  545.                  * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
  546.                  * TYPE_RADIO_BUTTON. */
  547. {
  548.     register Button *butPtr;
  549.     Tk_Window tkwin = (Tk_Window) clientData;
  550.     Tk_Window new;
  551.  
  552.     if (argc < 2) {
  553.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  554.         argv[0], " pathName ?options?\"", (char *) NULL);
  555.     return TCL_ERROR;
  556.     }
  557.  
  558.     /*
  559.      * Create the new window.
  560.      */
  561.  
  562.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  563.     if (new == NULL) {
  564.     return TCL_ERROR;
  565.     }
  566.  
  567.     /*
  568.      * Initialize the data structure for the button.
  569.      */
  570.  
  571.     butPtr = (Button *) ckalloc(sizeof(Button));
  572.     butPtr->tkwin = new;
  573.     butPtr->display = Tk_Display(new);
  574.     butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
  575.         ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
  576.     butPtr->interp = interp;
  577.     butPtr->type = type;
  578.     butPtr->text = NULL;
  579.     butPtr->numChars = 0;
  580.     butPtr->underline = -1;
  581.     butPtr->textVarName = NULL;
  582.     butPtr->bitmap = None;
  583.     butPtr->imageString = NULL;
  584.     butPtr->image = NULL;
  585.     butPtr->selectImageString = NULL;
  586.     butPtr->selectImage = NULL;
  587.     butPtr->state = tkNormalUid;
  588.     butPtr->normalBorder = NULL;
  589.     butPtr->activeBorder = NULL;
  590.     butPtr->borderWidth = 0;
  591.     butPtr->relief = TK_RELIEF_FLAT;
  592.     butPtr->highlightWidth = 0;
  593.     butPtr->highlightBgColorPtr = NULL;
  594.     butPtr->highlightColorPtr = NULL;
  595.     butPtr->inset = 0;
  596.     butPtr->fontPtr = NULL;
  597.     butPtr->normalFg = NULL;
  598.     butPtr->activeFg = NULL;
  599.     butPtr->disabledFg = NULL;
  600.     butPtr->normalTextGC = None;
  601.     butPtr->activeTextGC = None;
  602.     butPtr->gray = None;
  603.     butPtr->disabledGC = None;
  604.     butPtr->copyGC = None;
  605.     butPtr->widthString = NULL;
  606.     butPtr->heightString = NULL;
  607.     butPtr->width = 0;
  608.     butPtr->height = 0;
  609.     butPtr->wrapLength = 0;
  610.     butPtr->padX = 0;
  611.     butPtr->padY = 0;
  612.     butPtr->anchor = TK_ANCHOR_CENTER;
  613.     butPtr->justify = TK_JUSTIFY_CENTER;
  614.     butPtr->indicatorOn = 0;
  615.     butPtr->selectBorder = NULL;
  616.     butPtr->indicatorSpace = 0;
  617.     butPtr->indicatorDiameter = 0;
  618.     butPtr->selVarName = NULL;
  619.     butPtr->onValue = NULL;
  620.     butPtr->offValue = NULL;
  621.     butPtr->cursor = None;
  622.     butPtr->command = NULL;
  623.     butPtr->takeFocus = NULL;
  624.     butPtr->flags = 0;
  625. #ifdef STk_CODE
  626.     butPtr->string = 0;
  627. #endif
  628.  
  629.     Tk_SetClass(new, classNames[type]);
  630.     Tk_CreateEventHandler(butPtr->tkwin,
  631.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  632.         ButtonEventProc, (ClientData) butPtr);
  633.     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
  634.         configFlags[type]) != TCL_OK) {
  635.     Tk_DestroyWindow(butPtr->tkwin);
  636.     return TCL_ERROR;
  637.     }
  638.  
  639. #ifdef STk_CODE
  640.     STk_sharp_dot_result(interp, Tk_PathName(butPtr->tkwin));
  641. #else
  642.     interp->result = Tk_PathName(butPtr->tkwin);
  643. #endif
  644.     return TCL_OK;
  645. }
  646.  
  647. /*
  648.  *--------------------------------------------------------------
  649.  *
  650.  * ButtonWidgetCmd --
  651.  *
  652.  *    This procedure is invoked to process the Tcl command
  653.  *    that corresponds to a widget managed by this module.
  654.  *    See the user documentation for details on what it does.
  655.  *
  656.  * Results:
  657.  *    A standard Tcl result.
  658.  *
  659.  * Side effects:
  660.  *    See the user documentation.
  661.  *
  662.  *--------------------------------------------------------------
  663.  */
  664.  
  665. static int
  666. ButtonWidgetCmd(clientData, interp, argc, argv)
  667.     ClientData clientData;    /* Information about button widget. */
  668.     Tcl_Interp *interp;        /* Current interpreter. */
  669.     int argc;            /* Number of arguments. */
  670.     char **argv;        /* Argument strings. */
  671. {
  672.     register Button *butPtr = (Button *) clientData;
  673.     int result = TCL_OK;
  674.     size_t length;
  675.     int c;
  676. #ifdef STk_CODE
  677.     int flag;
  678. #endif
  679.  
  680.     if (argc < 2) {
  681.     sprintf(interp->result,
  682.         "wrong # args: should be \"%.50s option ?arg arg ...?\"",
  683.         argv[0]);
  684.     return TCL_ERROR;
  685.     }
  686.     Tcl_Preserve((ClientData) butPtr);
  687.     c = argv[1][0];
  688.     length = strlen(argv[1]);
  689.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  690.         && (length >= 2)) {
  691.     if (argc != 3) {
  692.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  693.             argv[0], " cget option\"",
  694.             (char *) NULL);
  695.         goto error;
  696.     }
  697.     result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
  698.         (char *) butPtr, argv[2], configFlags[butPtr->type]);
  699.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  700.         && (length >= 2)) {
  701.     if (argc == 2) {
  702.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  703.             (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
  704.     } else if (argc == 3) {
  705.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  706.             (char *) butPtr, argv[2],
  707.             configFlags[butPtr->type]);
  708.     } else {
  709.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  710.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  711.     }
  712.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  713.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  714.     if (argc > 2) {
  715.         sprintf(interp->result,
  716.             "wrong # args: should be \"%.50s deselect\"",
  717.             argv[0]);
  718.         goto error;
  719.     }
  720. #ifdef STk_CODE
  721.     flag = (butPtr->string)? STk_STRINGIFY : 0;
  722. #endif
  723.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  724.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  725. #ifdef STk_CODE
  726.             flag |
  727. #endif
  728.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  729.         result = TCL_ERROR;
  730.         }
  731.     } else if (butPtr->flags & SELECTED) {
  732. #ifdef STk_CODE
  733.         if (Tcl_SetVar(interp, butPtr->selVarName, "#t",
  734.            flag|
  735. #else
  736.         if (Tcl_SetVar(interp, butPtr->selVarName, "",
  737. #endif
  738.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  739.         result = TCL_ERROR;
  740.         };
  741.     }
  742.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  743.         && (butPtr->type != TYPE_LABEL)) {
  744.     int i;
  745.  
  746.     if (argc > 2) {
  747.         sprintf(interp->result,
  748.             "wrong # args: should be \"%.50s flash\"",
  749.             argv[0]);
  750.         goto error;
  751.     }
  752.     if (butPtr->state != tkDisabledUid) {
  753.         for (i = 0; i < 4; i++) {
  754.         butPtr->state = (butPtr->state == tkNormalUid)
  755.             ? tkActiveUid : tkNormalUid;
  756.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  757.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  758.             : butPtr->normalBorder);
  759.         DisplayButton((ClientData) butPtr);
  760.  
  761.         /*
  762.          * Special note: must cancel any existing idle handler
  763.          * for DisplayButton;  it's no longer needed, and DisplayButton
  764.          * cleared the REDRAW_PENDING flag.
  765.          */
  766.  
  767.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  768.         XFlush(butPtr->display);
  769.         Tcl_Sleep(50);
  770.         }
  771.     }
  772.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  773.         && (butPtr->type > TYPE_LABEL)) {
  774.     if (argc > 2) {
  775.         sprintf(interp->result,
  776.             "wrong # args: should be \"%.50s invoke\"",
  777.             argv[0]);
  778.         goto error;
  779.     }
  780.     if (butPtr->state != tkDisabledUid) {
  781.         result = InvokeButton(butPtr);
  782.     }
  783.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  784.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  785.     if (argc > 2) {
  786.         sprintf(interp->result,
  787.             "wrong # args: should be \"%.50s select\"",
  788.             argv[0]);
  789.         goto error;
  790.     }
  791.     if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  792. #ifdef STk_CODE
  793.            ((butPtr->string)? STk_STRINGIFY:0) |
  794. #endif
  795.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  796.  
  797.         result = TCL_ERROR;
  798.     }
  799.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  800.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  801.     if (argc > 2) {
  802.         sprintf(interp->result,
  803.             "wrong # args: should be \"%.50s toggle\"",
  804.             argv[0]);
  805.         goto error;
  806.     }
  807.     if (butPtr->flags & SELECTED) {
  808.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  809. #ifdef STk_CODE
  810.                 ((butPtr->string)? STk_STRINGIFY:0) |
  811. #endif
  812.             TCL_GLOBAL_ONLY) == NULL) {
  813.         result = TCL_ERROR;
  814.         }
  815.     } else {
  816.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  817. #ifdef STk_CODE
  818.                 ((butPtr->string)? STk_STRINGIFY:0) |
  819. #endif
  820.             TCL_GLOBAL_ONLY) == NULL) {
  821.         result = TCL_ERROR;
  822.         }
  823.     }
  824.     } else {
  825.     sprintf(interp->result,
  826.         "bad option \"%.50s\": must be %s", argv[1],
  827.         optionStrings[butPtr->type]);
  828.     goto error;
  829.     }
  830.     Tcl_Release((ClientData) butPtr);
  831.     return result;
  832.  
  833.     error:
  834.     Tcl_Release((ClientData) butPtr);
  835.     return TCL_ERROR;
  836. }
  837.  
  838. /*
  839.  *----------------------------------------------------------------------
  840.  *
  841.  * DestroyButton --
  842.  *
  843.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  844.  *    to clean up the internal structure of a button at a safe time
  845.  *    (when no-one is using it anymore).
  846.  *
  847.  * Results:
  848.  *    None.
  849.  *
  850.  * Side effects:
  851.  *    Everything associated with the widget is freed up.
  852.  *
  853.  *----------------------------------------------------------------------
  854.  */
  855.  
  856. static void
  857. DestroyButton(butPtr)
  858.     Button *butPtr;        /* Info about button widget. */
  859. {
  860.     /*
  861.      * Free up all the stuff that requires special handling, then
  862.      * let Tk_FreeOptions handle all the standard option-related
  863.      * stuff.
  864.      */
  865.  
  866.     if (butPtr->textVarName != NULL) {
  867.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  868.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  869.         ButtonTextVarProc, (ClientData) butPtr);
  870.     }
  871.     if (butPtr->image != NULL) {
  872.     Tk_FreeImage(butPtr->image);
  873.     }
  874.     if (butPtr->selectImage != NULL) {
  875.     Tk_FreeImage(butPtr->selectImage);
  876.     }
  877.     if (butPtr->normalTextGC != None) {
  878.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  879.     }
  880.     if (butPtr->activeTextGC != None) {
  881.     Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  882.     }
  883.     if (butPtr->gray != None) {
  884.     Tk_FreeBitmap(butPtr->display, butPtr->gray);
  885.     }
  886.     if (butPtr->disabledGC != None) {
  887.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  888.     }
  889.     if (butPtr->copyGC != None) {
  890.     Tk_FreeGC(butPtr->display, butPtr->copyGC);
  891.     }
  892.     if (butPtr->selVarName != NULL) {
  893.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  894.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  895.         ButtonVarProc, (ClientData) butPtr);
  896.     }
  897.     Tk_FreeOptions(configSpecs, (char *) butPtr, butPtr->display,
  898.         configFlags[butPtr->type]);
  899.     Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
  900. }
  901.  
  902. /*
  903.  *----------------------------------------------------------------------
  904.  *
  905.  * ConfigureButton --
  906.  *
  907.  *    This procedure is called to process an argv/argc list, plus
  908.  *    the Tk option database, in order to configure (or
  909.  *    reconfigure) a button widget.
  910.  *
  911.  * Results:
  912.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  913.  *    returned, then interp->result contains an error message.
  914.  *
  915.  * Side effects:
  916.  *    Configuration information, such as text string, colors, font,
  917.  *    etc. get set for butPtr;  old resources get freed, if there
  918.  *    were any.  The button is redisplayed.
  919.  *
  920.  *----------------------------------------------------------------------
  921.  */
  922.  
  923. static int
  924. ConfigureButton(interp, butPtr, argc, argv, flags)
  925.     Tcl_Interp *interp;        /* Used for error reporting. */
  926.     register Button *butPtr;    /* Information about widget;  may or may
  927.                  * not already have values for some fields. */
  928.     int argc;            /* Number of valid entries in argv. */
  929.     char **argv;        /* Arguments. */
  930.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  931. {
  932.     XGCValues gcValues;
  933.     GC newGC;
  934.     unsigned long mask;
  935.     Tk_Image image;
  936.  
  937.  
  938.     /*
  939.      * Eliminate any existing trace on variables monitored by the button.
  940.      */
  941.  
  942.     if (butPtr->textVarName != NULL) {
  943.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  944.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  945.         ButtonTextVarProc, (ClientData) butPtr);
  946.     }
  947.     if (butPtr->selVarName != NULL) {
  948.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  949.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  950.         ButtonVarProc, (ClientData) butPtr);
  951.     }
  952.  
  953.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
  954.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  955.     return TCL_ERROR;
  956.     }
  957.  
  958.     /*
  959.      * A few options need special processing, such as setting the
  960.      * background from a 3-D border, or filling in complicated
  961.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  962.      */
  963.  
  964.     if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
  965.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  966.     } else {
  967.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  968.     if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
  969.         && (butPtr->state != tkDisabledUid)) {
  970.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  971.             "\": must be normal, active, or disabled", (char *) NULL);
  972.         butPtr->state = tkNormalUid;
  973.         return TCL_ERROR;
  974.     }
  975.     }
  976.  
  977.     if (butPtr->highlightWidth < 0) {
  978.     butPtr->highlightWidth = 0;
  979.     }
  980.  
  981.     gcValues.font = butPtr->fontPtr->fid;
  982.     gcValues.foreground = butPtr->normalFg->pixel;
  983.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  984.     
  985.     /*
  986.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  987.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  988.      * that there's no problem with obscured areas).
  989.      */
  990.  
  991.     gcValues.graphics_exposures = False;
  992.     newGC = Tk_GetGC(butPtr->tkwin,
  993.         GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  994.         &gcValues);
  995.     if (butPtr->normalTextGC != None) {
  996.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  997.     }
  998.     butPtr->normalTextGC = newGC;
  999.  
  1000.     if (butPtr->activeFg != NULL) {
  1001.     gcValues.font = butPtr->fontPtr->fid;
  1002.     gcValues.foreground = butPtr->activeFg->pixel;
  1003.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  1004.     newGC = Tk_GetGC(butPtr->tkwin,
  1005.         GCForeground|GCBackground|GCFont, &gcValues);
  1006.     if (butPtr->activeTextGC != None) {
  1007.         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  1008.     }
  1009.     butPtr->activeTextGC = newGC;
  1010.     }
  1011.  
  1012.     if (butPtr->type != TYPE_LABEL) {
  1013.     gcValues.font = butPtr->fontPtr-> fid;
  1014.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  1015.     if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
  1016.         gcValues.foreground = butPtr->disabledFg->pixel;
  1017.         mask = GCForeground|GCBackground|GCFont;
  1018.     } else {
  1019.         gcValues.foreground = gcValues.background;
  1020.         if (butPtr->gray == None) {
  1021.         butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
  1022.             Tk_GetUid("gray50"));
  1023.         if (butPtr->gray == None) {
  1024.             return TCL_ERROR;
  1025.         }
  1026.         }
  1027.         gcValues.fill_style = FillStippled;
  1028.         gcValues.stipple = butPtr->gray;
  1029.         mask = GCForeground|GCFillStyle|GCStipple;
  1030.     }
  1031.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  1032.     if (butPtr->disabledGC != None) {
  1033.         Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  1034.     }
  1035.     butPtr->disabledGC = newGC;
  1036.     }
  1037.  
  1038.     if (butPtr->copyGC == None) {
  1039.     butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
  1040.     }
  1041.  
  1042.     if (butPtr->padX < 0) {
  1043.     butPtr->padX = 0;
  1044.     }
  1045.     if (butPtr->padY < 0) {
  1046.     butPtr->padY = 0;
  1047.     }
  1048.  
  1049.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  1050.     char *value;
  1051.  
  1052.     if (butPtr->selVarName == NULL) {
  1053.         butPtr->selVarName = (char *) ckalloc((unsigned)
  1054.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  1055.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  1056.     }
  1057.  
  1058.     /*
  1059.      * Select the button if the associated variable has the
  1060.      * appropriate value, initialize the variable if it doesn't
  1061.      * exist, then set a trace on the variable to monitor future
  1062.      * changes to its value.
  1063.      */
  1064.  
  1065.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1066.     butPtr->flags &= ~SELECTED;
  1067.     if (value != NULL) {
  1068.         if (strcmp(value, butPtr->onValue) == 0) {
  1069.         butPtr->flags |= SELECTED;
  1070.         }
  1071.     } else {
  1072.         if (Tcl_SetVar(interp, butPtr->selVarName,
  1073. #ifdef STk_CODE
  1074.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "#f",
  1075.             ((butPtr->string)? STk_STRINGIFY:0) |
  1076. #else
  1077.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  1078. #endif
  1079.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1080.         return TCL_ERROR;
  1081.         }
  1082.     }
  1083.     Tcl_TraceVar(interp, butPtr->selVarName,
  1084.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1085.         ButtonVarProc, (ClientData) butPtr);
  1086.     }
  1087.  
  1088.     /*
  1089.      * Get the images for the widget, if there are any.  Allocate the
  1090.      * new images before freeing the old ones, so that the reference
  1091.      * counts don't go to zero and cause image data to be discarded.
  1092.      */
  1093.  
  1094.     if (butPtr->imageString != NULL) {
  1095.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1096.         butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
  1097.     if (image == NULL) {
  1098.         return TCL_ERROR;
  1099.     }
  1100.     } else {
  1101.     image = NULL;
  1102.     }
  1103.     if (butPtr->image != NULL) {
  1104.     Tk_FreeImage(butPtr->image);
  1105.     }
  1106.     butPtr->image = image;
  1107.     if (butPtr->selectImageString != NULL) {
  1108.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1109.         butPtr->selectImageString, ButtonSelectImageProc,
  1110.         (ClientData) butPtr);
  1111.     if (image == NULL) {
  1112.         return TCL_ERROR;
  1113.     }
  1114.     } else {
  1115.     image = NULL;
  1116.     }
  1117.     if (butPtr->selectImage != NULL) {
  1118.     Tk_FreeImage(butPtr->selectImage);
  1119.     }
  1120.     butPtr->selectImage = image;
  1121.  
  1122.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)
  1123.         && (butPtr->textVarName != NULL)) {
  1124.     /*
  1125.      * The button must display the value of a variable: set up a trace
  1126.      * on the variable's value, create the variable if it doesn't
  1127.      * exist, and fetch its current value.
  1128.      */
  1129.  
  1130.     char *value;
  1131.  
  1132.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1133.     if (value == NULL) {
  1134.         if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1135. #ifdef STk_CODE
  1136.                 ((butPtr->string)? STk_STRINGIFY:0) |
  1137. #endif
  1138.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1139.         return TCL_ERROR;
  1140.         }
  1141.     } else {
  1142.         if (butPtr->text != NULL) {
  1143.         ckfree(butPtr->text);
  1144.         }
  1145.         butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1146.         strcpy(butPtr->text, value);
  1147.     }
  1148.     Tcl_TraceVar(interp, butPtr->textVarName,
  1149.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1150.         ButtonTextVarProc, (ClientData) butPtr);
  1151.     }
  1152.  
  1153.     if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
  1154.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
  1155.         &butPtr->width) != TCL_OK) {
  1156.         widthError:
  1157.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  1158.         return TCL_ERROR;
  1159.     }
  1160.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
  1161.         &butPtr->height) != TCL_OK) {
  1162.         heightError:
  1163.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  1164.         return TCL_ERROR;
  1165.     }
  1166.     } else {
  1167.     if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
  1168.         != TCL_OK) {
  1169.         goto widthError;
  1170.     }
  1171.     if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
  1172.         != TCL_OK) {
  1173.         goto heightError;
  1174.     }
  1175.     }
  1176.     ComputeButtonGeometry(butPtr);
  1177.  
  1178.     /*
  1179.      * Lastly, arrange for the button to be redisplayed.
  1180.      */
  1181.  
  1182.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1183.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1184.     butPtr->flags |= REDRAW_PENDING;
  1185.     }
  1186.  
  1187.     return TCL_OK;
  1188. }
  1189.  
  1190. /*
  1191.  *----------------------------------------------------------------------
  1192.  *
  1193.  * DisplayButton --
  1194.  *
  1195.  *    This procedure is invoked to display a button widget.  It is
  1196.  *    normally invoked as an idle handler.
  1197.  *
  1198.  * Results:
  1199.  *    None.
  1200.  *
  1201.  * Side effects:
  1202.  *    Commands are output to X to display the button in its
  1203.  *    current mode.  The REDRAW_PENDING flag is cleared.
  1204.  *
  1205.  *----------------------------------------------------------------------
  1206.  */
  1207.  
  1208. static void
  1209. DisplayButton(clientData)
  1210.     ClientData clientData;    /* Information about widget. */
  1211. {
  1212.     register Button *butPtr = (Button *) clientData;
  1213.     GC gc;
  1214.     Tk_3DBorder border;
  1215.     Pixmap pixmap;
  1216.     int x = 0;            /* Initialization only needed to stop
  1217.                  * compiler warning. */
  1218.     int y, relief;
  1219.     register Tk_Window tkwin = butPtr->tkwin;
  1220.     int width, height;
  1221.     int offset;            /* 0 means this is a label widget.  1 means
  1222.                  * it is a flavor of button, so we offset
  1223.                  * the text to make the button appear to
  1224.                  * move up and down as the relief changes. */
  1225.  
  1226.     butPtr->flags &= ~REDRAW_PENDING;
  1227.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  1228.     return;
  1229.     }
  1230.  
  1231.     border = butPtr->normalBorder;
  1232.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  1233.     gc = butPtr->disabledGC;
  1234.     } else if ((butPtr->state == tkActiveUid)
  1235.         && !Tk_StrictMotif(butPtr->tkwin)) {
  1236.     gc = butPtr->activeTextGC;
  1237.     border = butPtr->activeBorder;
  1238.     } else {
  1239.     gc = butPtr->normalTextGC;
  1240.     }
  1241.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  1242.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  1243.     border = butPtr->selectBorder;
  1244.     }
  1245.  
  1246.     /*
  1247.      * Override the relief specified for the button if this is a
  1248.      * checkbutton or radiobutton and there's no indicator.
  1249.      */
  1250.  
  1251.     relief = butPtr->relief;
  1252.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  1253.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  1254.         : TK_RELIEF_RAISED;
  1255.     }
  1256.  
  1257.     offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
  1258.  
  1259.     /*
  1260.      * In order to avoid screen flashes, this procedure redraws
  1261.      * the button in a pixmap, then copies the pixmap to the
  1262.      * screen in a single operation.  This means that there's no
  1263.      * point in time where the on-sreen image has been cleared.
  1264.      */
  1265.  
  1266.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  1267.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1268.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  1269.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1270.  
  1271.     /*
  1272.      * Display image or bitmap or text for button.
  1273.      */
  1274.  
  1275.     if (butPtr->image != None) {
  1276.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1277.  
  1278.     imageOrBitmap:
  1279.     switch (butPtr->anchor) {
  1280.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1281.         x = butPtr->inset + butPtr->indicatorSpace + offset;
  1282.         break;
  1283.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1284.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1285.             - width))/2;
  1286.         break;
  1287.         default:
  1288.         x = Tk_Width(tkwin) - butPtr->inset - width - offset;
  1289.         break;
  1290.     }
  1291.     switch (butPtr->anchor) {
  1292.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1293.         y = butPtr->inset + offset;
  1294.         break;
  1295.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1296.         y = ((int) (Tk_Height(tkwin) - height))/2;
  1297.         break;
  1298.         default:
  1299.         y = Tk_Height(tkwin) - butPtr->inset - height - offset;
  1300.         break;
  1301.     }
  1302.     if (relief == TK_RELIEF_RAISED) {
  1303.         x -= offset;
  1304.         y -= offset;
  1305.     } else if (relief == TK_RELIEF_SUNKEN) {
  1306.         x += offset;
  1307.         y += offset;
  1308.     }
  1309.     if (butPtr->image != NULL) {
  1310.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  1311.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
  1312.             x, y);
  1313.         } else {
  1314.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  1315.             x, y);
  1316.         }
  1317.     } else {
  1318.         XSetClipOrigin(butPtr->display, gc, x, y);
  1319.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  1320.             (unsigned int) width, (unsigned int) height, x, y, 1);
  1321.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  1322.     }
  1323.     y += height/2;
  1324.     } else if (butPtr->bitmap != None) {
  1325.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1326.     goto imageOrBitmap;
  1327.     } else {
  1328.     switch (butPtr->anchor) {
  1329.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1330.         x = butPtr->inset + butPtr->padX + butPtr->indicatorSpace
  1331.             + offset;
  1332.         break;
  1333.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1334.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1335.             - butPtr->textWidth))/2;
  1336.         break;
  1337.         default:
  1338.         x = Tk_Width(tkwin) - butPtr->inset - butPtr->padX
  1339.             - butPtr->textWidth - offset;
  1340.         break;
  1341.     }
  1342.     switch (butPtr->anchor) {
  1343.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1344.         y = butPtr->inset + butPtr->padY + offset;
  1345.         break;
  1346.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1347.         y = ((int) (Tk_Height(tkwin) - butPtr->textHeight))/2;
  1348.         break;
  1349.         default:
  1350.         y = Tk_Height(tkwin) - butPtr->inset - butPtr->padY
  1351.             - butPtr->textHeight - offset;
  1352.         break;
  1353.     }
  1354.     if (relief == TK_RELIEF_RAISED) {
  1355.         x -= offset;
  1356.         y -= offset;
  1357.     } else if (relief == TK_RELIEF_SUNKEN) {
  1358.         x += offset;
  1359.         y += offset;
  1360.     }
  1361.     TkDisplayText(butPtr->display, pixmap, butPtr->fontPtr,
  1362.         butPtr->text, butPtr->numChars, x, y, butPtr->textWidth,
  1363.         butPtr->justify, butPtr->underline, gc);
  1364.     y += butPtr->textHeight/2;
  1365.     }
  1366.  
  1367.     /*
  1368.      * Draw the indicator for check buttons and radio buttons.  At this
  1369.      * point x and y refer to the top-left corner of the text or image
  1370.      * or bitmap.
  1371.      */
  1372.  
  1373.     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1374.     int dim;
  1375.  
  1376.     dim = butPtr->indicatorDiameter;
  1377.     x -= butPtr->indicatorSpace;
  1378.     y -= dim/2;
  1379.     if (dim > 2*butPtr->borderWidth) {
  1380.         Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
  1381.             butPtr->borderWidth, 
  1382.             (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  1383.             TK_RELIEF_RAISED);
  1384.         x += butPtr->borderWidth;
  1385.         y += butPtr->borderWidth;
  1386.         dim -= 2*butPtr->borderWidth;
  1387.         if (butPtr->flags & SELECTED) {
  1388.         GC gc;
  1389.  
  1390.         gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
  1391.             ? butPtr->selectBorder : butPtr->normalBorder,
  1392.             TK_3D_FLAT_GC);
  1393.         XFillRectangle(butPtr->display, pixmap, gc, x, y,
  1394.             (unsigned int) dim, (unsigned int) dim);
  1395.         } else {
  1396.         Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
  1397.             dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
  1398.         }
  1399.     }
  1400.     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
  1401.     XPoint points[4];
  1402.     int radius;
  1403.  
  1404.     radius = butPtr->indicatorDiameter/2;
  1405.     points[0].x = x - butPtr->indicatorSpace;
  1406.     points[0].y = y;
  1407.     points[1].x = points[0].x + radius;
  1408.     points[1].y = points[0].y + radius;
  1409.     points[2].x = points[1].x + radius;
  1410.     points[2].y = points[0].y;
  1411.     points[3].x = points[1].x;
  1412.     points[3].y = points[0].y - radius;
  1413.     if (butPtr->flags & SELECTED) {
  1414.         GC gc;
  1415.  
  1416.         gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
  1417.             ? butPtr->selectBorder : butPtr->normalBorder,
  1418.             TK_3D_FLAT_GC);
  1419.         XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
  1420.             CoordModeOrigin);
  1421.     } else {
  1422.         Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
  1423.             4, butPtr->borderWidth, TK_RELIEF_FLAT);
  1424.     }
  1425.     Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
  1426.         (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  1427.         TK_RELIEF_RAISED);
  1428.     }
  1429.  
  1430.     /*
  1431.      * If the button is disabled with a stipple rather than a special
  1432.      * foreground color, generate the stippled effect.  If the widget
  1433.      * is selected and we use a different background color when selected,
  1434.      * must temporarily modify the GC.
  1435.      */
  1436.  
  1437.     if ((butPtr->state == tkDisabledUid)
  1438.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  1439.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1440.         && (butPtr->selectBorder != NULL)) {
  1441.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1442.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  1443.     }
  1444.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  1445.         butPtr->inset, butPtr->inset,
  1446.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  1447.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  1448.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1449.         && (butPtr->selectBorder != NULL)) {
  1450.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1451.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  1452.     }
  1453.     }
  1454.  
  1455.     /*
  1456.      * Draw the border and traversal highlight last.  This way, if the
  1457.      * button's contents overflow they'll be covered up by the border.
  1458.      */
  1459.  
  1460.     if (relief != TK_RELIEF_FLAT) {
  1461.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  1462.         butPtr->highlightWidth, butPtr->highlightWidth,
  1463.         Tk_Width(tkwin) - 2*butPtr->highlightWidth,
  1464.         Tk_Height(tkwin) - 2*butPtr->highlightWidth,
  1465.         butPtr->borderWidth, relief);
  1466.     }
  1467.     if (butPtr->highlightWidth != 0) {
  1468.     GC gc;
  1469.  
  1470.     if (butPtr->flags & GOT_FOCUS) {
  1471.         gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
  1472.     } else {
  1473.         gc = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
  1474.     }
  1475.     Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
  1476.     }
  1477.  
  1478.     /*
  1479.      * Copy the information from the off-screen pixmap onto the screen,
  1480.      * then delete the pixmap.
  1481.      */
  1482.  
  1483.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  1484.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  1485.         (unsigned) Tk_Height(tkwin), 0, 0);
  1486.     Tk_FreePixmap(butPtr->display, pixmap);
  1487. }
  1488.  
  1489. /*
  1490.  *--------------------------------------------------------------
  1491.  *
  1492.  * ButtonEventProc --
  1493.  *
  1494.  *    This procedure is invoked by the Tk dispatcher for various
  1495.  *    events on buttons.
  1496.  *
  1497.  * Results:
  1498.  *    None.
  1499.  *
  1500.  * Side effects:
  1501.  *    When the window gets deleted, internal structures get
  1502.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1503.  *
  1504.  *--------------------------------------------------------------
  1505.  */
  1506.  
  1507. static void
  1508. ButtonEventProc(clientData, eventPtr)
  1509.     ClientData clientData;    /* Information about window. */
  1510.     XEvent *eventPtr;        /* Information about event. */
  1511. {
  1512.     Button *butPtr = (Button *) clientData;
  1513.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1514.     goto redraw;
  1515.     } else if (eventPtr->type == ConfigureNotify) {
  1516.     /*
  1517.      * Must redraw after size changes, since layout could have changed
  1518.      * and borders will need to be redrawn.
  1519.      */
  1520.  
  1521.     goto redraw;
  1522.     } else if (eventPtr->type == DestroyNotify) {
  1523.     if (butPtr->tkwin != NULL) {
  1524.         butPtr->tkwin = NULL;
  1525.         Tcl_DeleteCommand(butPtr->interp,
  1526.             Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
  1527.     }
  1528.     if (butPtr->flags & REDRAW_PENDING) {
  1529.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  1530.     }
  1531.     DestroyButton(butPtr);
  1532.     } else if (eventPtr->type == FocusIn) {
  1533.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1534.         butPtr->flags |= GOT_FOCUS;
  1535.         if (butPtr->highlightWidth > 0) {
  1536.         goto redraw;
  1537.         }
  1538.     }
  1539.     } else if (eventPtr->type == FocusOut) {
  1540.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1541.         butPtr->flags &= ~GOT_FOCUS;
  1542.         if (butPtr->highlightWidth > 0) {
  1543.         goto redraw;
  1544.         }
  1545.     }
  1546.     }
  1547.     return;
  1548.  
  1549.     redraw:
  1550.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1551.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1552.     butPtr->flags |= REDRAW_PENDING;
  1553.     }
  1554. }
  1555.  
  1556. /*
  1557.  *----------------------------------------------------------------------
  1558.  *
  1559.  * ButtonCmdDeletedProc --
  1560.  *
  1561.  *    This procedure is invoked when a widget command is deleted.  If
  1562.  *    the widget isn't already in the process of being destroyed,
  1563.  *    this command destroys it.
  1564.  *
  1565.  * Results:
  1566.  *    None.
  1567.  *
  1568.  * Side effects:
  1569.  *    The widget is destroyed.
  1570.  *
  1571.  *----------------------------------------------------------------------
  1572.  */
  1573.  
  1574. static void
  1575. ButtonCmdDeletedProc(clientData)
  1576.     ClientData clientData;    /* Pointer to widget record for widget. */
  1577. {
  1578.     Button *butPtr = (Button *) clientData;
  1579.     Tk_Window tkwin = butPtr->tkwin;
  1580.  
  1581.     /*
  1582.      * This procedure could be invoked either because the window was
  1583.      * destroyed and the command was then deleted (in which case tkwin
  1584.      * is NULL) or because the command was deleted, and then this procedure
  1585.      * destroys the widget.
  1586.      */
  1587.  
  1588.     if (tkwin != NULL) {
  1589.     butPtr->tkwin = NULL;
  1590.     Tk_DestroyWindow(tkwin);
  1591.     }
  1592. }
  1593.  
  1594. /*
  1595.  *----------------------------------------------------------------------
  1596.  *
  1597.  * ComputeButtonGeometry --
  1598.  *
  1599.  *    After changes in a button's text or bitmap, this procedure
  1600.  *    recomputes the button's geometry and passes this information
  1601.  *    along to the geometry manager for the window.
  1602.  *
  1603.  * Results:
  1604.  *    None.
  1605.  *
  1606.  * Side effects:
  1607.  *    The button's window may change size.
  1608.  *
  1609.  *----------------------------------------------------------------------
  1610.  */
  1611.  
  1612. static void
  1613. ComputeButtonGeometry(butPtr)
  1614.     register Button *butPtr;    /* Button whose geometry may have changed. */
  1615. {
  1616.     int width, height;
  1617.  
  1618.     if (butPtr->highlightWidth < 0) {
  1619.     butPtr->highlightWidth = 0;
  1620.     }
  1621.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  1622.     butPtr->indicatorSpace = 0;
  1623.     if (butPtr->image != NULL) {
  1624.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1625.     imageOrBitmap:
  1626.     if (butPtr->width > 0) {
  1627.         width = butPtr->width;
  1628.     }
  1629.     if (butPtr->height > 0) {
  1630.         height = butPtr->height;
  1631.     }
  1632.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1633.         butPtr->indicatorSpace = height;
  1634.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1635.         butPtr->indicatorDiameter = (65*height)/100;
  1636.         } else {
  1637.         butPtr->indicatorDiameter = (75*height)/100;
  1638.         }
  1639.     }
  1640.     } else if (butPtr->bitmap != None) {
  1641.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1642.     goto imageOrBitmap;
  1643.     } else {
  1644.     butPtr->numChars = strlen(butPtr->text);
  1645.     TkComputeTextGeometry(butPtr->fontPtr, butPtr->text,
  1646.         butPtr->numChars, butPtr->wrapLength, &butPtr->textWidth,
  1647.         &butPtr->textHeight);
  1648.     width = butPtr->textWidth;
  1649.     height = butPtr->textHeight;
  1650.     if (butPtr->width > 0) {
  1651.         width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
  1652.     }
  1653.     if (butPtr->height > 0) {
  1654.         height = butPtr->height * (butPtr->fontPtr->ascent
  1655.             + butPtr->fontPtr->descent);
  1656.     }
  1657.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1658.         butPtr->indicatorDiameter = butPtr->fontPtr->ascent
  1659.             + butPtr->fontPtr->descent;
  1660.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1661.         butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
  1662.         }
  1663.         butPtr->indicatorSpace = butPtr->indicatorDiameter
  1664.         + XTextWidth(butPtr->fontPtr, "0", 1);
  1665.     }
  1666.     }
  1667.  
  1668.     /*
  1669.      * When issuing the geometry request, add extra space for the indicator,
  1670.      * if any, and for the border and padding, plus two extra pixels so the
  1671.      * display can be offset by 1 pixel in either direction for the raised
  1672.      * or lowered effect.
  1673.      */
  1674.  
  1675.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  1676.     width += 2*butPtr->padX;
  1677.     height += 2*butPtr->padY;
  1678.     }
  1679.     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
  1680.     width += 2;
  1681.     height += 2;
  1682.     }
  1683.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  1684.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  1685.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  1686. }
  1687.  
  1688. /*
  1689.  *----------------------------------------------------------------------
  1690.  *
  1691.  * InvokeButton --
  1692.  *
  1693.  *    This procedure is called to carry out the actions associated
  1694.  *    with a button, such as invoking a Tcl command or setting a
  1695.  *    variable.  This procedure is invoked, for example, when the
  1696.  *    button is invoked via the mouse.
  1697.  *
  1698.  * Results:
  1699.  *    A standard Tcl return value.  Information is also left in
  1700.  *    interp->result.
  1701.  *
  1702.  * Side effects:
  1703.  *    Depends on the button and its associated command.
  1704.  *
  1705.  *----------------------------------------------------------------------
  1706.  */
  1707.  
  1708. static int
  1709. InvokeButton(butPtr)
  1710.     register Button *butPtr;        /* Information about button. */
  1711. {
  1712. #ifdef STk_CODE
  1713.   int flag = (butPtr->string? STk_STRINGIFY : 0)|TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG;
  1714. #endif
  1715.  
  1716.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1717.     if (butPtr->flags & SELECTED) {
  1718.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1719. #ifdef STk_CODE
  1720.             flag) == NULL) {
  1721. #else
  1722.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1723. #endif
  1724.         return TCL_ERROR;
  1725.         }
  1726.     } else {
  1727.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1728. #ifdef STk_CODE
  1729.             flag) == NULL) {
  1730. #else
  1731.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1732. #endif
  1733.         return TCL_ERROR;
  1734.         }
  1735.     }
  1736.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1737.     if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1738. #ifdef STk_CODE
  1739.         flag) == NULL) {
  1740. #else
  1741.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1742. #endif
  1743.         return TCL_ERROR;
  1744.     }
  1745.     }
  1746.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1747.     return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
  1748.     }
  1749.     return TCL_OK;
  1750. }
  1751.  
  1752. /*
  1753.  *--------------------------------------------------------------
  1754.  *
  1755.  * ButtonVarProc --
  1756.  *
  1757.  *    This procedure is invoked when someone changes the
  1758.  *    state variable associated with a radio button.  Depending
  1759.  *    on the new value of the button's variable, the button
  1760.  *    may be selected or deselected.
  1761.  *
  1762.  * Results:
  1763.  *    NULL is always returned.
  1764.  *
  1765.  * Side effects:
  1766.  *    The button may become selected or deselected.
  1767.  *
  1768.  *--------------------------------------------------------------
  1769.  */
  1770.  
  1771.     /* ARGSUSED */
  1772. static char *
  1773. ButtonVarProc(clientData, interp, name1, name2, flags)
  1774.     ClientData clientData;    /* Information about button. */
  1775.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1776.     char *name1;        /* Name of variable. */
  1777.     char *name2;        /* Second part of variable name. */
  1778.     int flags;            /* Information about what happened. */
  1779. {
  1780.     register Button *butPtr = (Button *) clientData;
  1781.     char *value;
  1782.  
  1783.     /*
  1784.      * If the variable is being unset, then just re-establish the
  1785.      * trace unless the whole interpreter is going away.
  1786.      */
  1787.  
  1788.     if (flags & TCL_TRACE_UNSETS) {
  1789.     butPtr->flags &= ~SELECTED;
  1790.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1791.         Tcl_TraceVar(interp, butPtr->selVarName,
  1792.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1793.             ButtonVarProc, clientData);
  1794.     }
  1795.     goto redisplay;
  1796.     }
  1797.  
  1798.     /*
  1799.      * Use the value of the variable to update the selected status of
  1800.      * the button.
  1801.      */
  1802.  
  1803.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1804.     if (value == NULL) {
  1805.     value = "";
  1806.     }
  1807.     if (strcmp(value, butPtr->onValue) == 0) {
  1808.     if (butPtr->flags & SELECTED) {
  1809.         return (char *) NULL;
  1810.     }
  1811.     butPtr->flags |= SELECTED;
  1812.     } else if (butPtr->flags & SELECTED) {
  1813.     butPtr->flags &= ~SELECTED;
  1814.     } else {
  1815.     return (char *) NULL;
  1816.     }
  1817.  
  1818.     redisplay:
  1819.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1820.         && !(butPtr->flags & REDRAW_PENDING)) {
  1821.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1822.     butPtr->flags |= REDRAW_PENDING;
  1823.     }
  1824.     return (char *) NULL;
  1825. }
  1826.  
  1827. /*
  1828.  *--------------------------------------------------------------
  1829.  *
  1830.  * ButtonTextVarProc --
  1831.  *
  1832.  *    This procedure is invoked when someone changes the variable
  1833.  *    whose contents are to be displayed in a button.
  1834.  *
  1835.  * Results:
  1836.  *    NULL is always returned.
  1837.  *
  1838.  * Side effects:
  1839.  *    The text displayed in the button will change to match the
  1840.  *    variable.
  1841.  *
  1842.  *--------------------------------------------------------------
  1843.  */
  1844.  
  1845.     /* ARGSUSED */
  1846. static char *
  1847. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1848.     ClientData clientData;    /* Information about button. */
  1849.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1850.     char *name1;        /* Not used. */
  1851.     char *name2;        /* Not used. */
  1852.     int flags;            /* Information about what happened. */
  1853. {
  1854.     register Button *butPtr = (Button *) clientData;
  1855.     char *value;
  1856.  
  1857.     /*
  1858.      * If the variable is unset, then immediately recreate it unless
  1859.      * the whole interpreter is going away.
  1860.      */
  1861.  
  1862.     if (flags & TCL_TRACE_UNSETS) {
  1863.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1864.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1865. #ifdef STk_CODE
  1866.            (butPtr->string? STk_STRINGIFY: 0) |
  1867. #endif
  1868.             TCL_GLOBAL_ONLY);
  1869.         Tcl_TraceVar(interp, butPtr->textVarName,
  1870.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1871.             ButtonTextVarProc, clientData);
  1872.     }
  1873.     return (char *) NULL;
  1874.     }
  1875.  
  1876.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1877.     if (value == NULL) {
  1878.     value = "";
  1879.     }
  1880.     if (butPtr->text != NULL) {
  1881.     ckfree(butPtr->text);
  1882.     }
  1883.     butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1884.     strcpy(butPtr->text, value);
  1885.     ComputeButtonGeometry(butPtr);
  1886.  
  1887.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1888.         && !(butPtr->flags & REDRAW_PENDING)) {
  1889.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1890.     butPtr->flags |= REDRAW_PENDING;
  1891.     }
  1892.     return (char *) NULL;
  1893. }
  1894.  
  1895. /*
  1896.  *----------------------------------------------------------------------
  1897.  *
  1898.  * ButtonImageProc --
  1899.  *
  1900.  *    This procedure is invoked by the image code whenever the manager
  1901.  *    for an image does something that affects the size of contents
  1902.  *    of an image displayed in a button.
  1903.  *
  1904.  * Results:
  1905.  *    None.
  1906.  *
  1907.  * Side effects:
  1908.  *    Arranges for the button to get redisplayed.
  1909.  *
  1910.  *----------------------------------------------------------------------
  1911.  */
  1912.  
  1913. static void
  1914. ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1915.     ClientData clientData;        /* Pointer to widget record. */
  1916.     int x, y;                /* Upper left pixel (within image)
  1917.                      * that must be redisplayed. */
  1918.     int width, height;            /* Dimensions of area to redisplay
  1919.                      * (may be <= 0). */
  1920.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1921. {
  1922.     register Button *butPtr = (Button *) clientData;
  1923.  
  1924.     if (butPtr->tkwin != NULL) {
  1925.     ComputeButtonGeometry(butPtr);
  1926.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1927.         Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1928.         butPtr->flags |= REDRAW_PENDING;
  1929.     }
  1930.     }
  1931. }
  1932.  
  1933. /*
  1934.  *----------------------------------------------------------------------
  1935.  *
  1936.  * ButtonSelectImageProc --
  1937.  *
  1938.  *    This procedure is invoked by the image code whenever the manager
  1939.  *    for an image does something that affects the size of contents
  1940.  *    of the image displayed in a button when it is selected.
  1941.  *
  1942.  * Results:
  1943.  *    None.
  1944.  *
  1945.  * Side effects:
  1946.  *    May arrange for the button to get redisplayed.
  1947.  *
  1948.  *----------------------------------------------------------------------
  1949.  */
  1950.  
  1951. static void
  1952. ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1953.     ClientData clientData;        /* Pointer to widget record. */
  1954.     int x, y;                /* Upper left pixel (within image)
  1955.                      * that must be redisplayed. */
  1956.     int width, height;            /* Dimensions of area to redisplay
  1957.                      * (may be <= 0). */
  1958.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1959. {
  1960.     register Button *butPtr = (Button *) clientData;
  1961.  
  1962.     /*
  1963.      * Don't recompute geometry:  it's controlled by the primary image.
  1964.      */
  1965.  
  1966.     if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
  1967.         && Tk_IsMapped(butPtr->tkwin)
  1968.         && !(butPtr->flags & REDRAW_PENDING)) {
  1969.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1970.     butPtr->flags |= REDRAW_PENDING;
  1971.     }
  1972. }
  1973.